package com.jt.config;

import lombok.extern.slf4j.Slf4j;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName SecurityConfig
 * @Description 定义SpringSecurity配置类，用来配置认证规则
 *              当我们在执行登录操作时，底层逻辑：(了解)
 *              1)Filter(过滤器)
 *              2)AuthenticationManager(认证管理器)
 *              3)AuthenticationProvider(认证服务处理器)
 *              4)UserDetailsService(负责用户信息的获取及封装)
 * @Author Jiyao
 * @DATE 2021-11-25 17:19
 * @Version 1.0
 **/
@Slf4j
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 初始化加密对象
     * 此对象提供了一种不可解密的加密方式，相对于MD5更安全
     *
     * @return org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
     * @Date 17:27 2021/11/25
     * @Param []
     **/
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 定义认证管理器对象，完成用户信息的认证
     * 即判定用户身份信息的合法性，在基于oauth2协议完成认证时，需要此对象，所以此处将此对象交给Spring管理
     *
     * @return org.springframework.security.authentication.AuthenticationManager
     * @throws Exception
     * @Date 17:33 2021/11/25
     * @Param []
     **/
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);//默认所有的请求都要认证
        //1、禁用跨域攻击
        http.csrf().disable();
        //2、放行所有资源的访问(后续可以基于选择对请求进行认证和放行)
        http.authorizeRequests().anyRequest().permitAll();
        //3、自定义登录成功和失败以后的处理逻辑（可选）
        //假设没有如下设置，登录成功会显示404
        http.formLogin()
                .successHandler(successHandler())
                .failureHandler(failureHandler());
    }

    /**
     * 定义认证成功处理器
     * 登录成功以后返回json字符串
     * @return org.springframework.security.web.authentication.AuthenticationSuccessHandler
     * @Date 18:01 2021/11/25
     * @Param []
     **/
    @Bean
    public AuthenticationSuccessHandler successHandler() {
        return new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request,//请求
                                                HttpServletResponse response,//响应
                                                Authentication authentication//认证
                                                ) throws IOException, ServletException {
                Object principal = authentication.getPrincipal();
                log.info("principle:" + principal);
                //构建map对象封装到要响应到的客户端数据
                Map<String, Object> map = new HashMap<>();
                map.put("state",200);
                map.put("message","login ok");
                //将map对象转换成json格式字符串响应到客户端
                writeJsonClient(response,map);
            }
        };
    }

    /**
     * 定义认证失败处理器
     * 登录失败以后返回json数据
     * @return org.springframework.security.web.authentication.AuthenticationFailureHandler
     * @Date 18:00 2021/11/25
     * @Param []
     **/
    @Bean
    public AuthenticationFailureHandler failureHandler() {
        return new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request,
                                                HttpServletResponse response,
                                                AuthenticationException exception) throws IOException, ServletException {
                //构建map对象封装到要响应到客户端的数据
                Map<String, Object> map = new HashMap<>();
                map.put("state", 500);
                map.put("message", "login error");
                //将map对象转换成json格式字符串响应到客户端
                writeJsonClient(response,map);
            }
        };
    }

    /**
     * map对象转换成json格式字符串响应到客户端
     * @Date 18:13 2021/11/25
     * @Param [response, map]
     * @return void
     **/
    private void writeJsonClient(HttpServletResponse response, Map<String, Object> map) throws IOException {
        //转换json格式
        String json = new ObjectMapper().writeValueAsString(map);
        //设置响应数据的编码格式
        response.setCharacterEncoding("utf-8");
        //设置响应数据的类型
        response.setContentType("application/json; charset=UTF-8");
        //将数据响应到客户端->流对象
        PrintWriter out = response.getWriter();
        out.println(json);
        out.flush();//刷新
    }
}
